{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "befdd4c8-6e36-482d-b074-29c1c7610911",
   "metadata": {},
   "outputs": [],
   "source": [
    "# File: ribbon.ipynb\n",
    "# Code: Claude Code and Codex\n",
    "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n",
    "# License: Apache v2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c82f299b-60a0-4683-9ea1-a4c982ca20c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from frontend import App\n",
    "\n",
    "# create an app\n",
    "app = App.create(\"ribbon\")\n",
    "\n",
    "# create a tall rectangular ribbon mesh\n",
    "height = 12.0\n",
    "V, F = app.mesh.rectangle(\n",
    "    res_x=4, width=0.15, height=height, ex=[1, 0, 0], ey=[0, 0, 1]\n",
    ")\n",
    "app.asset.add.tri(\"ribbon\", V, F)\n",
    "\n",
    "# create a sphere tetrahedral mesh as the weight\n",
    "V, F, T = app.mesh.icosphere(r=0.35, subdiv_count=4).tetrahedralize()\n",
    "app.asset.add.tet(\"sphere\", V, F, T)\n",
    "\n",
    "# create a scene\n",
    "scene = app.scene.create()\n",
    "\n",
    "# add invisible hemisphere bowl to collect ribbons\n",
    "scene.add.invisible.sphere([0, 1, 0], 1.0).invert().hemisphere()\n",
    "\n",
    "# add heavy sphere that will fall and disturb the ribbons\n",
    "sphere = scene.add(\"sphere\").at(0, 1 + height, 0)\n",
    "sphere.param.set(\"density\", 2000)\n",
    "sphere.pin().pull().move_by([0, -height / 2, 0], 0, 2).unpin(2)\n",
    "\n",
    "# add grid of ribbons arranged in sinusoidal pattern\n",
    "N, scale = 5, 0.25\n",
    "for i, j in np.ndindex((N, N)):\n",
    "    x, y = scale * (i - N // 2), scale * (j - N // 2)\n",
    "    r = np.sin(np.sqrt(x * x + y * y)) ** 2\n",
    "    obj = (\n",
    "        scene.add(\"ribbon\").rotate(90.0, \"x\").at(x, 0.005 + r + height / 2, y).jitter()\n",
    "    )\n",
    "    obj.param.set(\"bend\", 1e3).set(\"young-mod\", 5000).set(\"friction\", 0.5)\n",
    "\n",
    "# set preview options\n",
    "opts = {\"lookat\": [0, 1, 0], \"eyeup\": 0.5, \"fov\": 10}\n",
    "\n",
    "# compile the scene and report stats\n",
    "scene = scene.build().report()\n",
    "\n",
    "# preview the initial scene\n",
    "scene.preview(options=opts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0c6a26d6-e564-4db8-b018-3535ba49d0e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a new session with the compiled scene\n",
    "session = app.session.create(scene)\n",
    "\n",
    "# set session parameters with air resistance\n",
    "session.param.set(\"frames\", 480).set(\"air-density\", 2e-3)\n",
    "\n",
    "# build this session\n",
    "session = session.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87b9df4c-98cc-4bb9-ad93-90f0484e87d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# start the simulation and live-preview the results\n",
    "session.start().preview(options=opts)\n",
    "\n",
    "# also show simulation logs in realtime\n",
    "session.stream()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6b6d3137-fc35-4db2-b9cd-4abeb149b266",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create an animation from the simulation results\n",
    "session.animate(options=opts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26750281-9f97-4bfa-8184-dcb82083c630",
   "metadata": {},
   "outputs": [],
   "source": [
    "# export the animation to file\n",
    "session.export.animation()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2133446c-d669-434d-94e9-d4f9c0e35bd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is for CI\n",
    "if app.ci:\n",
    "    assert session.finished()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
